//
//  GB2ShapeCache.h
//  
//  Loads physics sprites created with http://www.PhysicsEditor.de
//
//  Generic Shape Cache for box2d
//
//  Copyright by Andreas Loew 
//      http://www.PhysicsEditor.de
//      http://texturepacker.com
//      http://www.code-and-web.de
//  
//  All rights reserved.
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//  
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//  
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#include "GB2ShapeCache.h"

CCPoint GB2ShapeCache::CGPointFromString(std::string str)
{
	size_t position = str.find("{");
	str.replace(position,2,"");
	position = str.find("}");
	str.replace(position -1,2,"");
	position = str.find(",");

	std::string x = str.substr(0,position - 1);
	std::string y = str.substr(position + 1,str.length());
	return ccp(atof(x.c_str()),atof(y.c_str()));
}

bool BodyDef::init()
{
    fixtures = NULL;
    return true;
}

BodyDef::~BodyDef()
{
    delete fixtures;
}

GB2ShapeCache * GB2ShapeCache::sharedShapeCache()
{
    static GB2ShapeCache *shapeCache = NULL;
    if(!shapeCache)
    {
		shapeCache = new GB2ShapeCache();
		shapeCache->init();
    }
    return shapeCache;
}

bool GB2ShapeCache::init()
{
	shapeObjects_ = new CCMutableDictionary<std::string, BodyDef*>();
    return true;
}

GB2ShapeCache::~GB2ShapeCache()
{
	shapeObjects_->release();
}

void GB2ShapeCache::addFixturesToBody(b2Body *body, std::string shape)
{
	BodyDef *so = shapeObjects_->objectForKey(shape);
    assert(so);
    
    FixtureDef *fix = so->fixtures;
    while(fix)
    {
        body->CreateFixture(&fix->fixture);
        fix = fix->next;
    }
}

CCPoint GB2ShapeCache::anchorPointForShape(std::string shape)
{
    BodyDef *bd = shapeObjects_->objectForKey(shape);
    assert(bd);
    return bd->anchorPoint;
}

void GB2ShapeCache::addShapesWithFile(std::string plist)
{
	const char* path = CCFileUtils::fullPathFromRelativePath(plist.c_str());
	CCDictionary<std::string, CCObject*> *dictionary = CCFileUtils::dictionaryWithContentsOfFileThreadSafe(path);
	
	CCDictionary<std::string, CCObject*> *metadataDict = (CCDictionary<std::string, CCObject*>*)dictionary->objectForKey("metadata");
	CCString *s_format = (CCString*)metadataDict->objectForKey("format");
	int format = s_format->toInt();

	CCString *s_ptm_ratio = (CCString*)(metadataDict->objectForKey("ptm_ratio"));
	ptmRatio_ = s_ptm_ratio->toFloat();

	assert(format == 1);

	CCDictionary<std::string, CCObject*> *bodyDict = (CCDictionary<std::string, CCObject*>*)dictionary->objectForKey("bodies");
	b2Vec2 vertices[b2_maxPolygonVertices];
	
	//CCDictionary<std::string, CCObject*>::CCObjectMapIter it;
	//CCObjectMapIter it;
//	CCObjectMap map = bodyDict->getMap();
	std::string key = "";
    CCDictionary<std::string, CCObject*> *bodyData;// = (CCDictionary<std::string, CCObject*>*)bodyDict->objectForKey(it->first);
    bodyDict->begin();
//	for(it = map.begin(); it!= map.end();++it)
	while( (bodyData = (CCDictionary<std::string, CCObject*>*)bodyDict->next(&key)) )
	{
        // get the body data
        
		// create body object
		BodyDef *bodyDef = new BodyDef();
		bodyDef->init();
		bodyDef->autorelease();

		CCString *s_bodyData = (CCString*)(bodyData->objectForKey("anchorpoint"));
		bodyDef->anchorPoint = CGPointFromString(s_bodyData->toStdString());
		
        // iterate through the fixtures
        CCMutableArray<CCObject*> *fixtureList = (CCMutableArray<CCObject*>*)bodyData->objectForKey("fixtures");
        FixtureDef **nextFixtureDef = &(bodyDef->fixtures);

		CCDictionary<std::string, CCObject*> *fixtureData;
		GB2_FOREACH_DICT(fixtureList,fixtureData)
		if (fixtureList &&
			fixtureList->count() > 0)
		/*for(CCObject** arr = fixtureList->data->arr,
			**end = fixtureList->data->arr + fixtureList->data->num-1;
			arr <= end
			&& ((fixtureData = (CCDictionary<std::string, CCObject*>*)*arr) != NULL);
			arr++)*/
        for(int i=0;i<fixtureList->count();i++)
		{
			fixtureData = (CCDictionary<std::string, CCObject*>*)fixtureList->getObjectAtIndex(i);
			if(!fixtureData)
				continue;
            b2FixtureDef basicData;
            
            basicData.filter.categoryBits = ((CCString*)fixtureData->objectForKey("filter_categoryBits"))->toInt();
            basicData.filter.maskBits =((CCString*)fixtureData->objectForKey("filter_maskBits"))->toInt();
			basicData.filter.groupIndex = ((CCString*)fixtureData->objectForKey("filter_groupIndex"))->toInt();
			basicData.friction = ((CCString*)fixtureData->objectForKey("friction"))->toFloat();
            basicData.density = ((CCString*)fixtureData->objectForKey("density"))->toFloat();
            basicData.restitution = ((CCString*)fixtureData->objectForKey("restitution"))->toFloat();
			basicData.isSensor = ((CCString*)fixtureData->objectForKey("isSensor"))->toInt()!=0;
            //int callbackData = ((CCString*)fixtureData->objectForKey("userdataCbValue"))->toInt();
			
			CCString* fixtureType = (CCString*)fixtureData->objectForKey("fixture_type");
			
            // read polygon fixtures. One concave fixture may consist of several convex polygons
			if(!strcmp(fixtureType->toStdString().c_str(),"POLYGON"))
            {
				CCMutableArray<> *polygonsArray = (CCMutableArray<>*)fixtureData->objectForKey("polygons");
				CCMutableArray<> *polygonArray;
			
				GB2_FOREACH_ARRAY(polygonsArray,polygonArray)
				{
                    FixtureDef *fix = new FixtureDef();
                    fix->fixture = basicData; // copy basic data
                    //fix->callbackData = callbackData;

                    b2PolygonShape *polyshape = new b2PolygonShape();
                    int vindex = 0;
                    
                    assert(polygonArray->count() <= b2_maxPolygonVertices);
					CCString *pointString;
					GB2_FOREACH_STR(polygonArray,pointString)
					{
                        CCPoint offset = CGPointFromString(pointString->toStdString());
                        vertices[vindex].x = (offset.x / ptmRatio_) ; 
                        vertices[vindex].y = (offset.y / ptmRatio_) ; 
                        vindex++;
					}
					
                    polyshape->Set(vertices, vindex);
                    fix->fixture.shape = polyshape;
                    
                    // create a list
                    *nextFixtureDef = fix;
                    nextFixtureDef = &(fix->next);
				}
			}
			else if(!strcmp(fixtureType->toStdString().c_str(),"CIRCLE"))
			{ 
                FixtureDef *fix = new FixtureDef();
                fix->fixture = basicData; // copy basic data
                //fix->callbackData = callbackData;

				CCDictionary<std::string, CCObject*> *circleData = (CCDictionary<std::string, CCObject*>*)(fixtureData->objectForKey("circle"));
                
				b2CircleShape *circleShape = new b2CircleShape();
				circleShape->m_radius = ((CCString*)circleData->objectForKey("radius"))->toFloat() / ptmRatio_;
				CCPoint p = CGPointFromString(((CCString*)fixtureData->objectForKey("center"))->toStdString());
                circleShape->m_p = b2Vec2(p.x / ptmRatio_, p.y / ptmRatio_);
                fix->fixture.shape = circleShape;
               
				// create a list
                *nextFixtureDef = fix;
                nextFixtureDef = &(fix->next);
			}
			else
			{
                // unknown type
                assert(0);
			}
		}
        // add the body element to the hash
		shapeObjects_->setObject(bodyDef, key);
	}
}
